#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <err.h>
#include <mcheck.h>
#include "./lib/parStr.h"
#include "./lib/cr.h"
#include "./lib/cw.h"

#include "key.h"
#include "data.h"
#include "dict.h"
#include "hash.h"
#include "crw.h"

struct sdict {
  crw *cinta;
  int cant;    
};



/* ------------------------------------------------- */

dict 
dict_empty(void)
{
  dict dr;
  int i=0;
  
  dr=(dict)malloc(sizeof(struct sdict));
 
  dr->cinta=(crw *)malloc(sizeof(crw)*(HASH_TAM));
  while (i<HASH_TAM)
  {
  dr->cinta[i]=crw_create();
  /*  dr->w->k=key_empty();
  dr->w->d=data_empty();
  */
  i++;
  }
	dr->cant=0;


  return dr;
}

/*
  DESC: Constructor del tipo. Crea un diccionario vacio.
  PRE: { d = NULL }
  d = dict_empty(f);
  POS: {  Hay memoria  ==> d --> empty
  ! Hay memoria  ==> d == NULL }
*/


dict
dict_fromFile(char * f)
{

  /*creo un array que contenga los elementos del archivo. */
  
  char *kk;
  char *dd;
  cr c1;
  parStr e;

  dict dicc=dict_empty();
  key kkk;
  data ddd;
  
  

  c1 = cr_create(f);

  /* No crea el archivo*/
  if (c1 == NULL)
    {
      errx(EXIT_FAILURE, "Error al crear cinta de lectura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  /* Arranca la cinta de lectura*/
  c1 = cr_arr(c1);
  if (c1 == NULL)
    {
      errx(EXIT_FAILURE, "Error al arrancar cinta de lectura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  
  /* Carga los valores contenidos en el diccionario*/
  
  while (c1 != NULL && !cr_fin(c1)) /* Si c1 es NULL es porque no pudo arrancar la cinta */
    {
      
      /* Carga la primera linea*/
      e = cr_elecor(c1);
      
      /* Si no pudo cargar la primera linea*/
      
      if (e == NULL)
	{
	  errx(EXIT_FAILURE, "No se devolvio elecorr"
	       ", archivo %s, linea %i", __FILE__, __LINE__);
	}
      
      kk=parStr_fst(e); 
      dd=parStr_snd(e); 
      kkk=key_empty();
      ddd=data_empty();

      if (kk == NULL || dd == NULL)
	{
	  errx(EXIT_FAILURE, "no se devolvio algun string" 
	       ", archivo %s, linea %i", __FILE__, __LINE__); 
	}
      
      /* Agregar los datos al array */
      assert(key_copyStr(kkk,kk));
      assert(data_copyStr(ddd,dd));
      
      dict_add(dicc,kkk,ddd);
      
	/* Libera el contenido de las variables */
	free(kk);
	free(dd); 
	kkk=NULL;
	ddd=NULL;
/*
	kkk=key_destroy(kkk);
	ddd=data_destroy(ddd);*/

  
      /*libera e*/
      e = parStr_destroy(e);
      
      /*da el avance de line*/
      cr_av(c1);
    }
  
  if (c1!=NULL)
    {
      c1 = cr_destroy(c1);	
    }
  
  return dicc;
  
}

/*
  DESC: Constructor del tipo. Crea un diccionario a partir de un archivo.
  Toma el nombre del archivo.
  PRE: { d = NULL }
  d = dict_fromFile(f);
  POS: {  Hay memoria /\ ! Error de Archivo ==> d --> fromFile(f) 
  ! Hay memoria \/   Error de Archivo ==> d == NULL }
*/

dict 
dict_destroy(dict d)
{
  int i;
  
  for(i=0;i<(HASH_TAM);i++)
    {
      ((d->cinta)[i])=crw_destroy((d->cinta)[i]);
			   
		}
   
  free(d->cinta);        /*Liberamos el arreglo de la estructura word*/
  (d->cinta)=NULL;       /* Desapuntar el puntero al arreglo */

  
  free(d);           /*Liberamos cant y el puntero al arreglo*/
  d=NULL;

  
  return d;
}

/*
  DESC: Destructor del tipo. d cambia.
  PRE: { d --> D }
  d = dict_destroy(d);
  POS: { Se destruye D /\ d == NULL }
*/

bool
dict_toFile(char *f , const dict d)
{
  cw c2;
  parStr e;
  int i=0;

  bool b;
  char *str1;
  char *str2;
  /*crea la cinta*/
  c2=cw_create(f);
  
  if (c2 == NULL)
    {
      errx(EXIT_FAILURE, "Error al crear cinta de escritura" 
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }
  
  /*arranca la cinta*/
  c2=cw_arr(c2);
  
  if(c2 == NULL)
    {
      errx(EXIT_FAILURE, "Error al arrancar cinta de escritura"
	   ", archivo %s, linea %i", __FILE__, __LINE__);
    }

  /*arranco la cinta de caracteres*/
/*  crw_arr(d->cinta);*/

  /*  while ( i<(d->cant) && c2 != NULL)*/
  /*ejecuta el bucle hasta llegar al ultimo elemento de la cinta*/
while (i<=(HASH_TAM-1))
{
	((d->cinta)[i])=crw_arr((d->cinta)[i]);
	while (!(crw_isempty(d->cinta[i])) && !(crw_fin((d->cinta)[i])) )/*&& c2 != NULL)*/
    {
/*      str1=key_toStr((d->w)[i].k);
	str2=data_toStr((d->w)[i].d);*/
        
        str1=key_toStr(crw_elecor_key((d->cinta)[i]));
        str2=data_toStr(crw_elecor_data((d->cinta)[i]));

        e=parStr_create(str1,str2);
/*avanzo la cinta */

   
      
    	  if (e == NULL)
				{
	  		errx(EXIT_FAILURE, "No se devolvio elecor"
	  	     ", archivo %s, linea %i", __FILE__, __LINE__);
				}
	  
      
    	  c2 = cw_insert(c2, e);
				((d->cinta)[i])=crw_av((d->cinta)[i]);      
    	  e = parStr_destroy(e);

      
    	  free(str1);
    	  free(str2);
    }
i++;
}	
	b = (c2 != NULL);
	if (b)	
	    c2 = cw_destroy(c2);
	
  return b;
}

/*
  DESC: Guarda el diccionario en el archivo f.
  PRE: { d --> D }
  b = dict_toFile(f, d);
  POS: { ! Error de Archivo ==>  b /\ D guardado en archivo f 
  Error de Archivo ==> !b }
*/


dict
dict_add(dict d, const key k, const data e)
{
  int pos_hash=0;
  char *str1;

  
  str1=key_toStr(k);
  pos_hash=hash(str1);
  
	((d->cinta)[pos_hash])=crw_insert(k,e,(d->cinta)[pos_hash]);
        
      /* Agrandador de diccionario y liberador de pepe*/
  d->cant=(d->cant)+1;
  
  free(str1);
     
  return d;
}

/*
  DESC: Agrega en el diccionario el dato d con la clave k.
  Si la clave ya existe no hace nada.
  Si agrega no utiliza k ni e (copia).
  PRE: { d --> D /\ k --> K /\ e --> E /\ !exist(D,K)}
  d = dict_add(d, k, e);
  POS: { d --> add(D,K,E) }
*/

bool
dict_exist(const dict d, const key k)
{
  if (d->cant==0)
	return FALSE;
  else
  	return (dict_search(d,k)!=NULL);
}

/*
  DESC: Busca el dato con clave k si existe. Si no existe devuelve False.
  PRE: { d --> D /\ k --> K }
  b = dict_exist(d, k);
  POS: { (D =  add(D',K,E) /\ b ) \/ 
  (D != add(D',K,E) /\ !b ) }
*/

data
dict_search(const dict d, const key k)
{
  
  int i;

  i=lsearch(d,k);
   
	if (i!=-1)
	    return (crw_elecor_data((d->cinta)[i]));
	else
	    return NULL;
 
}


/*
  DESC: Busca y trae el dato con clave k si existe. Si no devuelve NULL.
  Crea copia del dato.
  PRE: { d --> D /\ k --> K /\ e = NULL }
  e = dict_search(d, k);
  POS: { (D =  add(D',K,E) /\ e --> E ) \/ 
  (D != add(D',K,E) /\ e = NULL ) }
*/

dict
dict_del(dict d, const key k)
{
  
  int c;
  
  c=lsearch(d,k);
  
  if(c!=-1)
  {   
    (d->cinta)[c]=crw_del((d->cinta)[c]);
    /*((d->cinta)[c])=crw_destroy(((d->cinta)[c]));*/
   (d->cant)=(d->cant)-1;
  }

  return d;
}

/*
  DESC: Borra el dato con clave k. Si no esta devuelve null. 
  Destruye contenidos.
  PRE: { d --> D /\ k --> K /\ e = NULL }
  d = dict_del(d, k);
  POS: { ( D  = add(D',K,E) /\ d --> D') \/
  ( D != add(D',K,E) /\ d --> D }
*/

int
dict_length(dict d)
{
  return (d->cant);
}

/*
  DESC: Devuelve la cantidad de estradas en el diccionario. 
  PRE: { d --> D }
  l = dict_length(d);
  POS: { l = length(D) }
*/


int
lsearch(dict d, key k)
{
  int i=0;
  char *str1;
  char *str2;

  str1=key_toStr(k);
  i=hash(str1);
	
  /*  while (i<d->cant) */
	if (!(crw_isempty((d->cinta)[i])))
	{ 
	 	((d->cinta)[i])=crw_arr((d->cinta)[i]);
  	/*str1=key_toStr(k);*/

	  while (!(crw_fin((d->cinta)[i])))
  	  {
      

  	    str2=key_toStr(crw_elecor_key((d->cinta)[i]));
      
		 	 if (strcmp(str1,str2)==0)
				{
		 	   free(str1);
		 	   free(str2);
		 	   return i;
		 	 	}
	  
	 	 (d->cinta)[i]=crw_av((d->cinta)[i]);
	 	 free(str2);
	  
   	 }
		/*free(str1);*/
  }
		free(str1);
  return (-1);

/*regresa -1 si no se encontro, si no regresa la posicion del arreglo en el que se encuentra  la cinta en la que esta la palabra, y ademas deja en el elecor la palabra encontrada*/

}

int
listado(dict d, int i)
{
  
  return(crw_tam((d->cinta)[i]));
}
/*
int i=0;
 char *str1;
system("clear");
printf("\n------------------------------------------------------");
printf("\n---------------------INICIO---------------------------");
printf("\n------------------------------------------------------");
	while (i<d->cant) 
	{
	  str1=key_toStr((crw_elecor_key(d->cinta)[i]));
	  printf("\n%s",str1);
	  i=i+1;
	  free(str1);
	}
printf("\n------------------------------------------------------");
printf("\n-----------------------FIN----------------------------");
printf("\n------------------------------------------------------\n\n");
*/

